﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using TheVerge.Base.Platform;
using TheVerge.Base;

namespace TheVerge.Base.Models
{
    public interface IVMEntries
    {
        bool HasItems { get; }

        int ItemsCount { get; }

        string DisplayName { get; }

        void LoadMore();
        void Refresh();
    }

    public abstract class VMEntries : AVM
    {
        public List<VMEntries<ArticleEntry>> Entries { get; set; }

        public VMEntries<ArticleEntry> SelectedEntries
        {
            get { return GetValue(() => SelectedEntries); }
            set { SetValue(value, () => SelectedEntries); }
        }

        public VMEntries()
        {
            Entries = new List<VMEntries<ArticleEntry>>();
        }

        protected void SelectEntryFromDisplayName(string displayName)
        {
            foreach (var e in Entries)
            {
                if (e.DisplayName == displayName)
                {
                    SelectedEntries = e;
                    return;
                }
            }
        }
    }

    public abstract class VMEntries<T> : AVM, IVMEntries where T : IEntry
    {

        /// <summary>
        /// Represents the last page loaded. Pages are 1 based
        /// so if this is 0, nothing has been loaded.
        /// </summary>
        protected int LastPageLoaded;
        protected int TotalPages;

        #region Public Properties
        public abstract string DisplayName { get; }

        public ObservableCollection<T> Items { get; protected set; }
        public IEnumerable<T> ListItems { get { return Items.Skip(5); } }

        public T First { get { return Get(0); } }
        public T Second { get { return Get(1); } }
        public T Third { get { return Get(2); } }
        public T Fourth { get { return Get(3); } }
        public T Fifth { get { return Get(4); } }

        public int ItemsCount { get { return Items.Count; } }

        public bool HasItems
        {
            get
            {
                if (IsRefreshing || IsRefreshing) return true;

                return ItemsCount > 0;
            }
        }

        public int TotalItems
        {
            get
            {
                if (Items == null) return 0;
                return Items.Count;
            }
        }

        private bool IsMakingRequest { get { return IsLoading || IsRefreshing; } }

        public bool IsLoading
        {
            get { return GetValue(() => IsLoading); }
            set
            {
                SetValue(value, () => IsLoading);
                Notify(() => HasItems);
            }
        }

        public bool IsRefreshing
        {
            get { return GetValue(() => IsRefreshing); }
            set
            {
                SetValue(value, () => IsRefreshing);
                Notify(() => HasItems);
            }
        }
        #endregion

        public VMEntries()
        {
            Items = new ObservableCollection<T>();
            TotalPages = 1;
        }

        /// <summary>
        /// Refresh will clear out all of the existing items, and load
        /// the most recent articles.
        /// </summary>
        public void Refresh()
        {
            if (IsMakingRequest) return;

            IsRefreshing = true;
            var lastPageLoaded = LastPageLoaded;
            LastPageLoaded = 0;
            LoadMoreImp(result =>
            {
                UI.Invoke(() =>
                    {
                        if (null != result)
                        {
                            LastPageLoaded = result.CurrentPage;
                            TotalPages = result.TotalPages;
                            Items.Clear();
                            AddUniqueEntries(result.Entries);
                        }
                        else
                        {
                            LastPageLoaded = lastPageLoaded;
                        }
                        IsRefreshing = false;
                    });
            }, e => { IsRefreshing = false; });
        }

        public void LoadMore()
        {
            if (IsMakingRequest) return;

            if (LastPageLoaded == TotalPages) return; //-- There's no more info to load

            IsLoading = true;
            LoadMoreImp(result =>
            {
                if (null != result)
                {
                    UI.Invoke(() =>
                    {
                        LastPageLoaded = result.CurrentPage;
                        TotalPages = result.TotalPages;
                        AddUniqueEntries(result.Entries);
                        IsLoading = false;
                    });
                }
            }, e =>
            {
                IsLoading = false;
            });
        }

        private void AddUniqueEntries(T[] entries)
        {
            foreach (var i in entries)
            {
                var currentItem = Items.FirstOrDefault(x => x.Id == i.Id);

                if (currentItem == null)
                    Items.Add(i);
                else
                {
                    int idx = Items.IndexOf(currentItem);
                    Items.Remove(currentItem);
                    Items.Insert(idx, i);
                }
            }

            Notify(() => First);
            Notify(() => Second);
            Notify(() => Third);
            Notify(() => Fourth);
            Notify(() => Fifth);
            Notify(() => ListItems);
        }

        protected abstract void LoadMoreImp(Action<APIResponse<T>> OnFinished, Action<Exception> OnError);

        private T Get(int index)
        {
            try
            {
                var x = Items.Skip(index).FirstOrDefault();
                return x;
            }
            catch { return default(T); }
        }

        public override string ToString()
        {
            return DisplayName;
        }
    }
}
